<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h3 data-lake-id="JUMn5" id="JUMn5"><br></h3>
  <h3 data-lake-id="sja3r" id="sja3r"><span data-lake-id="u0dcd6a9e" id="u0dcd6a9e" style="color: rgba(25, 26, 31, 0.9)">问题发现</span></h3>
  <p data-lake-id="u7ff9a090" id="u7ff9a090"><span data-lake-id="u5a0da457" id="u5a0da457" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">本次是针对某个产品做风控定价的回流导入，相当于把风控策略基于风险测算出的价格通过数据同步的方式回流到我们的定价配置表中。</span></p>
  <p data-lake-id="u13993369" id="u13993369"><span data-lake-id="u886424ec" id="u886424ec" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u0c0a9e30" id="u0c0a9e30"><span data-lake-id="u00687d21" id="u00687d21" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">本次总计回流的数据大概有16万条，在此之前，我们的定价配置表中有不到2万条数据，所以，在回流之前的方案设计中，我们就考虑到可能因为数据量增大带来的一些问题了。所以，在回流过程中，我们在第一批回流了2万左右条数据之后，没有继续操作 ，开始观察线上的监控报警情况。</span></p>
  <p data-lake-id="u826e4ea6" id="u826e4ea6"><span data-lake-id="ue765ffcf" id="ue765ffcf" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="uf0df9737" id="uf0df9737"><span data-lake-id="u36ff79c3" id="u36ff79c3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">经过观察发现，我们的报价接口的 RT 有明显的增加，时间点和我们做数据回流的时间点刚好吻合：</span></p>
  <p data-lake-id="u3b4194f5" id="u3b4194f5"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1668685108394-2fb2fb72-c827-41c7-871e-2a571667a0ba.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_16%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h3 data-lake-id="K85bA" id="K85bA"><span data-lake-id="ub8b53934" id="ub8b53934" style="color: rgba(25, 26, 31, 0.9)">问题排查</span></h3>
  <p data-lake-id="u698e5909" id="u698e5909"><span data-lake-id="u049700a6" id="u049700a6" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">所以第一时间开始排查接口的链路情况了，很明显这次 RT 升高就是因为我们的数据回流导致的，于是首先登录服务器通过 Arthas （使用手册：</span><a href="https://arthas.aliyun.com/doc/index.html" target="_blank" data-lake-id="u56a4fe74" id="u56a4fe74"><span data-lake-id="u433fbbbc" id="u433fbbbc">https://arthas.aliyun.com/doc/index.html</span></a><span data-lake-id="ua614fc01" id="ua614fc01" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)"> </span><span data-lake-id="u37aede68" id="u37aede68" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">）看了下接口耗时的主要操作，验证下是不是在查询定价配置表的地方。</span></p>
  <p data-lake-id="ue0f0cf15" id="ue0f0cf15"><span data-lake-id="u38708496" id="u38708496" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="uc248ca62" id="uc248ca62"><span data-lake-id="u7790af5a" id="u7790af5a" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">先下载 arthas： </span></p>
  <p data-lake-id="ubebdde43" id="ubebdde43"><span data-lake-id="u7bd9b6c2" id="u7bd9b6c2" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <pre lang="java"><code>
curl -L http://start.alibaba-inc.com/install.sh | sh
</code></pre>
  <p data-lake-id="u4c0e9de3" id="u4c0e9de3"><span data-lake-id="u009beed3" id="u009beed3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u3addacf8" id="u3addacf8"><span data-lake-id="u63586b04" id="u63586b04" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">然后运行：</span></p>
  <pre lang="java"><code>
sh as.sh 
</code></pre>
  <p data-lake-id="ub325c083" id="ub325c083"><span data-lake-id="ufad5dabf" id="ufad5dabf" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="ua6c38fe7" id="ua6c38fe7"><span data-lake-id="u4c03116a" id="u4c03116a" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">使用 trace 命令查看接口耗时：</span></p>
  <pre lang="java"><code>
[arthas@1658]$ trace com.alibaba.fin.pricing.**.PriceCalculateService trial '#cost &gt; 50' -n 3
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 427 ms, listenerId: 6
`---ts=2021-11-08 15:10:24;thread_name=HSFBizProcessor-DEFAULT-8-thread-224;id=2d7c1;is_daemon=true;priority=10;TCCL=com.taobao.pandora.boot.loader.LaunchedURLClassLoader@783e6358;trace_id=2132e43116363554229592404e58b8;rpc_id=9.40.6
    `---[264.85838ms] com.alibaba.fin.pricing.**.service.PriceCalculateService:trial()
        +---[0.012009ms] com.alibaba.fin.pricing.**.request.PriceCalculateRequest:getTenant() #95
        +---[0.001564ms] com.alibaba.fin.pricing.**.request.PriceCalculateRequest:getProduct() #96
        ...
        ...
        ...
        +---[221.884809ms] com.alibaba.fin.pricing.*.ExercisePriceDomainService:queryMatchedEffectiveExercisePrice() #167
        +---[0.002242ms] com.alibaba.fin.pricing.**.service.PriceQueryRequest:&lt;init&gt;() #170
        `---[0.012586ms] com.alibaba.fin.pricing.**.service.PriceCalculateService:getTieredPrice() #170
</code></pre>
  <p data-lake-id="u5932a7b3" id="u5932a7b3"><span data-lake-id="u654d8c3c" id="u654d8c3c" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u4a76239c" id="u4a76239c"><span data-lake-id="u2064e919" id="u2064e919" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">经过分析发现，方法总时长264ms，其中有221ms 是耗费在ExercisePriceDomainService:queryMatchedEffectiveExercisePrice() 方法上，这个方法就是查询定价配置表获取定价配置信息的。</span></p>
  <p data-lake-id="u601d6850" id="u601d6850"><span data-lake-id="ud8a4e4c3" id="ud8a4e4c3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u34c5b5f5" id="u34c5b5f5"><span data-lake-id="ub90803b3" id="ub90803b3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">而且，通过 arthas监控发现，并不是所有的报价过程都是耗时很长，只有部分报价的时间比较大，还有一些是没有什么特殊变化的。</span></p>
  <p data-lake-id="uff4ab6e8" id="uff4ab6e8"><span data-lake-id="u418f1495" id="u418f1495" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="ua96714f3" id="ua96714f3"><span data-lake-id="u824dd124" id="u824dd124" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">这种情况，可以继续使用 Arthas的 watch 方法，查看那些耗时比较长的请求的具体出入参，看能不能找到规律。</span></p>
  <p data-lake-id="ub6ddaf1f" id="ub6ddaf1f"><span data-lake-id="uff41b078" id="uff41b078" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="uf8a8856d" id="uf8a8856d"><span data-lake-id="u2be01c00" id="u2be01c00" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">但是因为我们的应用接入了某联调平台，所以直接到上面看一下接口的请求情况及耗时就行了：</span></p>
  <p data-lake-id="u42949682" id="u42949682"><span data-lake-id="ua9daa8ec" id="ua9daa8ec" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u1fcd39bb" id="u1fcd39bb"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1668685108378-345890d6-7663-444a-bbc7-19fd74f048d6.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_34%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u386c123e" id="u386c123e"><span data-lake-id="ufe7d0ad3" id="ufe7d0ad3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="ucb542fe2" id="ucb542fe2"><span data-lake-id="u7701ff45" id="u7701ff45" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">经过分析发现，RT 较长的请求都是归属于同一个产品的，这个产品就是本次要做千人千面定价的这个产品，其他产品的耗时没有明显变化。</span></p>
  <p data-lake-id="ub17e88f3" id="ub17e88f3"><span data-lake-id="u3bb2266c" id="u3bb2266c" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u816356c8" id="u816356c8"><span data-lake-id="u49f532d8" id="u49f532d8" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">这时候，问题就基本定位到了，只需要分析这个产品的 SQL 语句和其他产品的 SQL 有什么区别就行了。</span></p>
  <p data-lake-id="u1e835aec" id="u1e835aec"><span data-lake-id="u06913988" id="u06913988" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">经过分析发现，这个产品的查询时因为要做千人千面的定价，在查询条件中和其他产品一样包含了 product、price_scene、group_code 等字段，但是还比其他产品多了 payer_id、payee_id 等字段。</span></p>
  <p data-lake-id="u35e58a63" id="u35e58a63"><span data-lake-id="ueb6a62b5" id="ueb6a62b5" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u6eed2cac" id="u6eed2cac"><span data-lake-id="u9e8f6df3" id="u9e8f6df3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">因为我们之前给product、price_scene、group_code加过联合索引，所以这个查询慢的 SQL 也是可以走到索引的，但是还是耗时比较长。</span></p>
  <p data-lake-id="u7a9ab07e" id="u7a9ab07e"><span data-lake-id="ub632a14d" id="ub632a14d" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u7f788a31" id="u7f788a31"><strong><span data-lake-id="ucd95f1b7" id="ucd95f1b7" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">主要原因是虽然命中了索引，但是索引查询过滤之后，还是有好几万条数据需要通过payer_id、payee_id 来做过滤。也就是说，之前的索引对于这个产品来说，区分度不高！</span></strong></p>
  <h3 data-lake-id="Qnw42" id="Qnw42"><span data-lake-id="u16714ce3" id="u16714ce3" style="color: rgba(25, 26, 31, 0.9)">问题解决</span></h3>
  <p data-lake-id="ue68aa2cd" id="ue68aa2cd"><span data-lake-id="uae3fc309" id="uae3fc309" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">所以，解决办法也比较简单，那就是修改索引，我后来重建创建了一个索引，针对基于payer_id、payee_id这种定价方式创建了一个新的索引。</span></p>
  <p data-lake-id="u205873d9" id="u205873d9"><span data-lake-id="ucc082ee3" id="ucc082ee3" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u2e7d1640" id="u2e7d1640"><span data-lake-id="u95af0614" id="u95af0614" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">索引发布后，接口 RT 有明显下降：</span></p>
  <p data-lake-id="u23ba5941" id="u23ba5941"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1668685108344-ff18d881-49ee-4633-9c01-8fedbc80cc0e.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u6c9e0113" id="u6c9e0113"><span data-lake-id="u1a2d6220" id="u1a2d6220" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">平均 RT 比之前还要低了一些。</span></p>
  <p data-lake-id="uf2d0d460" id="uf2d0d460"><span data-lake-id="u8fa65e53" id="u8fa65e53" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="ufd432168" id="ufd432168"><span data-lake-id="ufda7ae09" id="ufda7ae09" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">之后我们陆续把16万条数据全部回流了，RT 无明显变化。</span></p>
  <h3 data-lake-id="Y0P1l" id="Y0P1l"><span data-lake-id="u85aea141" id="u85aea141" style="color: rgba(25, 26, 31, 0.9)">总结</span></h3>
  <p data-lake-id="u0dfdcf37" id="u0dfdcf37"><br></p>
  <p data-lake-id="uec8961a1" id="uec8961a1"><span data-lake-id="u3a963035" id="u3a963035" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">因为我们提前预判了本次回流可能带来的问题，所以在第一批数据回流之后，就主动去观察了相关监控，第一时间发现了问题。</span></p>
  <p data-lake-id="u2c592655" id="u2c592655"><span data-lake-id="u8e7cfb5b" id="u8e7cfb5b" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="uedc51091" id="uedc51091"><span data-lake-id="u5b12a7b0" id="u5b12a7b0" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">在排查时，有针对性的去对指定接口的指定方法做了监控和分析，很快发现了 SQL 在用了索引之后，仍然耗时较长的问题。</span></p>
  <p data-lake-id="u1a85d3dc" id="u1a85d3dc"><span data-lake-id="uf6405e13" id="uf6405e13" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="u3976613d" id="u3976613d"><span data-lake-id="u36a54bdf" id="u36a54bdf" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">解决方案也比较简单，就是创建区分度更高的索引就可以了。</span></p>
  <p data-lake-id="u84bf9ef5" id="u84bf9ef5"><span data-lake-id="u8f251fb6" id="u8f251fb6" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">​</span><br></p>
  <p data-lake-id="ue02bc4cf" id="ue02bc4cf"><span data-lake-id="u7da59886" id="u7da59886" class="lake-fontsize-12" style="color: rgba(25, 26, 31, 0.9)">思考：随着系统的不断发展，数据库表结构不断变化，一些以前创建的索引，可能慢慢变得区分度没那么高了，会使得一些查询变慢。所以，在做一些数据库结构变更，代码逻辑改动的时候，需要考虑到索引是否需要调整和新增的问题。</span></p>
 </body>
</html>